package cn.bugstack.gateway.bind;

import cn.bugstack.gateway.session.Configuration;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.config.utils.ReferenceConfigCache;
import org.apache.dubbo.rpc.service.GenericService;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class GenericReferenceRegistry {

    /*
     * @description 泛型引用注册中心
     * 配置与注册相分离，方便如果不是dubbo的场景，只需重新改变注册的代码就行
     * config 只负责配置，不关注怎么注册的
     * 注册可以根据不同的需求，如：dubbo、MQ、redis
     */
    private final Configuration configuration;

    // 缓存：ReferenceConfigCache
    private final ConcurrentMap<String, ReferenceConfigCache> referenceConfigCacheMap = new ConcurrentHashMap<>();
    // 缓存：接口名——GenericService
    private final ConcurrentMap<String, GenericService> genericServiceCache = new ConcurrentHashMap<>();

    public GenericReferenceRegistry(Configuration configuration) {
        this.configuration = configuration;
    }

    // 保存：方法——泛化服务工厂
    private final ConcurrentMap<String, GenericReferenceProxyFactory> knownGenericReference = new ConcurrentHashMap<>();

    public void addGenericReference(String application, String interfaceName, String methodName) {
        // 获取应用配置和注册中心配置
        ApplicationConfig applicationConfig = configuration.getApplicationConfig(application);
        RegistryConfig registryConfig = configuration.getRegistryConfig(application);
        ReferenceConfig<GenericService> referenceConfig = configuration.getReferenceConfig(interfaceName);


        // 缓存两次：GenericService 和 ReferenceConfigCache
        GenericService genericService = genericServiceCache.computeIfAbsent(interfaceName, k -> {
            ReferenceConfigCache cache = referenceConfigCacheMap.computeIfAbsent(interfaceName, key -> {
                // 启动 Dubbo 服务
                DubboBootstrap bootstrap = DubboBootstrap.getInstance();
                bootstrap.application(applicationConfig).registry(registryConfig).reference(referenceConfig).start();
                return ReferenceConfigCache.getCache();
            });
            return cache.get(referenceConfig);
        });

        knownGenericReference.put(methodName, GenericReferenceProxyFactory.getInstance(genericService, methodName));
    }

    public IGenericReference getGenericReference(String methodName) {
        GenericReferenceProxyFactory genericReferenceProxyFactory = knownGenericReference.get(methodName);
        if (genericReferenceProxyFactory == null) {
            throw new RuntimeException("No such method: " + methodName);
        }
        return genericReferenceProxyFactory.getGenericReference();
    }
}